#include "cocoa/ccocoatoolkit.h"
#include "cocoa/ccocoaautoreleasepool.h"
#include "cocoa/ccocoawindow.h"
#include "cocoa/ccocoaexposehelper.h"
#include "cocoa/caglcontext.h"

#include <carbon/carbon.h>

namespace buola { namespace gui { namespace cocoa {

CCocoaToolkit::CCocoaToolkit()
{
    
}

CCocoaToolkit::~CCocoaToolkit()
{
    Finish();
}

void CCocoaToolkit::Start()
{
    CToolkit::Start();
    CCocoaAutoReleasePool lPool;
    NSApp = [NSApplication sharedApplication];
    [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
    id menubar = [[NSMenu new] autorelease];
    id appMenuItem = [[NSMenuItem new] autorelease];
    [menubar addItem:appMenuItem];
    [NSApp setMainMenu:menubar];
    id appMenu = [[NSMenu new] autorelease];
    id appName = [[NSProcessInfo processInfo] processName];
    id quitTitle = [@"Quit " stringByAppendingString:appName];
    id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle
    action:@selector(terminate:) keyEquivalent:@"q"] autorelease];
    [appMenu addItem:quitMenuItem];
    [appMenuItem setSubmenu:appMenu];
}

void CCocoaToolkit::Synchronize()
{
    //probably nothing to do
    //msg_info() << "synchronizing\n";
}

///////////////////////////
// root and proxy window
//
void CCocoaToolkit::QueryPointer(CPhysicalWindow *pWindow,CPoint_d &pInWindow,CPoint_d &pInScreen)
{
    ///\todo implement this!!
}

CPhysicalWindow *CCocoaToolkit::GetNewPhysicalWindow(CWindow *pWindow)
{
    msg_info() << "creating physical window!\n";
    return new CCocoaWindow(pWindow);
}

CClipboard *CCocoaToolkit::GetClipboard(int)
{
    ///\todo implement this!!
    return nullptr;
}

int CCocoaToolkit::GetDefaultDepth()
{
    ///\todo check that it is indeed 32!!
    return 32;
}

img::CSurface *CCocoaToolkit::GetNewPixmap(const CSize_i&,int pDepth,unsigned char *pData,int pStride)
{
    ///\todo implement this!!
    return nullptr;
}

EKeyMod CCocoaToolkit::GetMods(NSUInteger pMods)
{
    EKeyMod lRet=KEYMOD_NONE;
    
    if(pMods&NSShiftKeyMask)
        lRet|=KEYMOD_SHIFT;
    if(pMods&NSControlKeyMask)
        lRet|=KEYMOD_CONTROL;
    if(pMods&NSAlternateKeyMask)
        lRet|=KEYMOD_ALT;
    if (pMods&NSCommandKeyMask)
        lRet|=KEYMOD_COMMAND;

    return lRet;
}

EKeyLock CCocoaToolkit::GetLocks(NSUInteger pMods)
{
    EKeyLock lRet=KEYLOCK_NONE;
    
    if(pMods&NSAlphaShiftKeyMask)
        lRet|=KEYLOCK_CAPS;

    return lRet;
}

EKeyCode CCocoaToolkit::GetKey(int pCode)
{
    switch(pCode)
    {
    case kVK_ANSI_A:
        return KEY_A;
    case kVK_ANSI_S:
        return KEY_S;
    case kVK_ANSI_D:
        return KEY_D;
    case kVK_ANSI_F:
        return KEY_F;
    case kVK_ANSI_H:
        return KEY_H;
    case kVK_ANSI_G:
        return KEY_G;
    case kVK_ANSI_Z:
        return KEY_Z;
    case kVK_ANSI_X:
        return KEY_X;
    case kVK_ANSI_C:
        return KEY_C;
    case kVK_ANSI_V:
        return KEY_V;
    case kVK_ANSI_B:
        return KEY_B;
    case kVK_ANSI_Q:
        return KEY_Q;
    case kVK_ANSI_W:
        return KEY_W;
    case kVK_ANSI_E:
        return KEY_E;
    case kVK_ANSI_R:
        return KEY_R;
    case kVK_ANSI_Y:
        return KEY_Y;
    case kVK_ANSI_T:
        return KEY_T;
    case kVK_ANSI_1:
        return KEY_1;
    case kVK_ANSI_2:
        return KEY_2;
    case kVK_ANSI_3:
        return KEY_3;
    case kVK_ANSI_4:
        return KEY_4;
    case kVK_ANSI_6:
        return KEY_6;
    case kVK_ANSI_5:
        return KEY_5;
    case kVK_ANSI_Equal:
        return KEY_EQUAL;
    case kVK_ANSI_9:
        return KEY_9;
    case kVK_ANSI_7:
        return KEY_7;
    case kVK_ANSI_Minus:
        return KEY_MINUS;
    case kVK_ANSI_8:
        return KEY_8;
    case kVK_ANSI_0:
        return KEY_0;
    case kVK_ANSI_RightBracket:
        return KEY_RBRACKET;
    case kVK_ANSI_O:
        return KEY_O;
    case kVK_ANSI_U:
        return KEY_U;
    case kVK_ANSI_LeftBracket:
        return KEY_LBRACKET;
    case kVK_ANSI_I:
        return KEY_I;
    case kVK_ANSI_P:
        return KEY_P;
    case kVK_ANSI_L:
        return KEY_L;
    case kVK_ANSI_J:
        return KEY_J;
    case kVK_ANSI_Quote:
        return KEY_APOSTROPHE;
    case kVK_ANSI_K:
        return KEY_K;
    case kVK_ANSI_Semicolon:
        return KEY_SEMICOLON;
    case kVK_ANSI_Backslash:
        return KEY_BACKSLASH;
    case kVK_ANSI_Comma:
        return KEY_COMMA;
    case kVK_ANSI_Slash:
        return KEY_SLASH;
    case kVK_ANSI_N:
        return KEY_N;
    case kVK_ANSI_M:
        return KEY_M;
    case kVK_ANSI_Period:
        return KEY_PERIOD;
    case kVK_ANSI_Grave:
        return KEY_GRAVE;
    case kVK_ANSI_KeypadDecimal:
        return KEY_KPPERIOD;
    case kVK_ANSI_KeypadMultiply:
        return KEY_KPMULTIPLY;
    case kVK_ANSI_KeypadPlus:
        return KEY_KPPLUS;
    case kVK_ANSI_KeypadClear:
        return KEY_KPDEL;
    case kVK_ANSI_KeypadDivide:
        return KEY_KPDIVIDE;
    case kVK_ANSI_KeypadEnter:
        return KEY_KPENTER;
    case kVK_ANSI_KeypadMinus:
        return KEY_KPMINUS;
    case kVK_ANSI_KeypadEquals:
        return KEY_KPEQUAL;
    case kVK_ANSI_Keypad0:
        return KEY_KP0;
    case kVK_ANSI_Keypad1:
        return KEY_KP1;
    case kVK_ANSI_Keypad2:
        return KEY_KP2;
    case kVK_ANSI_Keypad3:
        return KEY_KP3;
    case kVK_ANSI_Keypad4:
        return KEY_KP4;
    case kVK_ANSI_Keypad5:
        return KEY_KP5;
    case kVK_ANSI_Keypad6:
        return KEY_KP6;
    case kVK_ANSI_Keypad7:
        return KEY_KP7;
    case kVK_ANSI_Keypad8:
        return KEY_KP8;
    case kVK_ANSI_Keypad9:
        return KEY_KP9;

    case kVK_Return:
        return KEY_ENTER;
    case kVK_Tab:
        return KEY_TAB;
    case kVK_Space:
        return KEY_SPACE;
    case kVK_Delete:
        return KEY_DEL;
    case kVK_Escape:
        return KEY_ESC;
    case kVK_Command:
        return KEY_COMMAND;
    case kVK_Shift:
        return KEY_LSHIFT;
    case kVK_CapsLock:
        return KEY_CAPSLOCK;
    case kVK_Option:
        return KEY_LALT;
    case kVK_Control:
        return KEY_LCONTROL;
    case kVK_RightShift:
        return KEY_RSHIFT;
    case kVK_RightOption:
        return KEY_ALTGR;
    case kVK_RightControl:
        return KEY_RCONTROL;
    case kVK_Function:
        return KEY_FUNCTION;
    case kVK_F17:
        return KEY_F17;
    case kVK_VolumeUp:
        return KEY_VOLUMEUP;
    case kVK_VolumeDown:
        return KEY_VOLUMEDOWN;
    case kVK_Mute:
        return KEY_AUDIOMUTE;
    case kVK_F18:
        return KEY_F18;
    case kVK_F19:
        return KEY_F19;
    case kVK_F20:
        return KEY_F20;
    case kVK_F5:
        return KEY_F5;
    case kVK_F6:
        return KEY_F6;
    case kVK_F7:
        return KEY_F7;
    case kVK_F3:
        return KEY_F3;
    case kVK_F8:
        return KEY_F8;
    case kVK_F9:
        return KEY_F9;
    case kVK_F11:
        return KEY_F11;
    case kVK_F13:
        return KEY_F13;
    case kVK_F16:
        return KEY_F16;
    case kVK_F14:
        return KEY_F14;
    case kVK_F10:
        return KEY_F10;
    case kVK_F12:
        return KEY_F12;
    case kVK_F15:
        return KEY_F15;
    case kVK_Help:
        return KEY_HELP;
    case kVK_Home:
        return KEY_HOME;
    case kVK_PageUp:
        return KEY_PGUP;
    case kVK_ForwardDelete:
        return KEY_DEL;
    case kVK_F4:
        return KEY_F4;
    case kVK_End:
        return KEY_END;
    case kVK_F2:
        return KEY_F2;
    case kVK_PageDown:
        return KEY_PGDN;
    case kVK_F1:
        return KEY_F1;
    case kVK_LeftArrow:
        return KEY_LEFT;
    case kVK_RightArrow:
        return KEY_RIGHT;
    case kVK_DownArrow:
        return KEY_DOWN;
    case kVK_UpArrow:
        return KEY_UP;

    case kVK_ISO_Section:
    case kVK_JIS_Yen:
    case kVK_JIS_Underscore:
    case kVK_JIS_KeypadComma:
    case kVK_JIS_Eisu:
    case kVK_JIS_Kana:
    default:
        return KEY_NONE;
    }
}

bool CCocoaToolkit::KeysEqual(const TKey &p1,const TKey &p2)
{
    ///\todo implement this!!
    return p1==p2;
}

///////////////////
// expose helper
//
long CCocoaToolkit::GetStockCursor(CPointer::EStock pIndex)
{
    ///\todo implement this!!
    return 0;
}

long CCocoaToolkit::GetCursor(unsigned char*,unsigned char*,int,int,int)
{
    ///\todo implement this!!
    return 0;
}

void CCocoaToolkit::GrabKey(const TKey&,EKeyMod,bool,CWindow*)
{
    ///\todo implement this!!
}

void CCocoaToolkit::UngrabKey(const TKey&,EKeyMod,CWindow*)
{
    ///\todo implement this!!
}

img::CGC *CCocoaToolkit::GetOpenGLGC()
{
    return new gl::COpenGLGC(new CAGLContext(nWithoutWindow));
}

CCocoaToolkit *get_cocoa_toolkit()
{
    static std::unique_ptr<CCocoaToolkit> lX11Toolkit;

    if(!lX11Toolkit)
    {
        lX11Toolkit.reset(new CCocoaToolkit);
        lX11Toolkit->Start();
    }

    return lX11Toolkit.get();
}

/*namespace cocoa*/ } /*namespace gui*/ } /*namespace buola*/ }

namespace buola { namespace gui {

CToolkit *get_toolkit()
{
    return cocoa::get_cocoa_toolkit();
}

/*namespace gui*/ } /*namespace buola*/ }
